<?php


namespace helper\calc;


class Scanner
{
    //operatoren          nummern               wörter                  leerzeichen
    const PATTERN = '/^([!,\+\-\*\/\^%\(\)]|\d*\.\d+|\d+\.\d*|\d+|[a-z_A-Zπ]+[a-z_A-Z0-9]*|[ \t]+)/';

    const ERR_EMPTY = '空循环或者无限循环: `%s`',
        ERR_MATCH = '语法错误: `%s`';

    protected $tokens = array(0);

    protected $lookup = array(
        '+' => 65,
        '-' => 66,
        '/' => 68,
        '%' => 69,
        '^' => 70,
        '*' => 67,
        '(' => 8,
        ')' => 16,
        '!' => 73,
        ',' => 32
    );

    public function __construct($input)
    {
        $prev = new Token(64, 'noop');

        while (trim($input) !== '') {
            if (!preg_match(self::PATTERN, $input, $match)) {
                // syntax fehler
                throw new \Exception(sprintf(self::ERR_MATCH, substr($input, 0, 10)));
            }

            if (empty($match[1]) && $match[1] !== '0') {
                // leerer fund -> endlosschleife vermeiden
                throw new \Exception(sprintf(self::ERR_EMPTY, substr($input, 0, 10)));
            }

            // aktuellen wert von input abziehen
            $input = substr($input, strlen($match[1]));

            if (($value = trim($match[1])) === '') {
                // leerzeichen ignorieren
                continue;
            }

            if (is_numeric($value)) {
                if ($prev->type === 16)
                    $this->tokens[] = new Token(67, '*');

                $this->tokens[] = $prev = new Token(1, (float)$value);
                continue;
            }

            switch ($type = isset($this->lookup[$value]) ? $this->lookup[$value] : 2) {
                case 65:
                    if ($prev->type & 64 || $prev->type == 8) $type = 71;
                    break;

                case 66:
                    if ($prev->type & 64 || $prev->type == 8) $type = 72;
                    break;

                case 8:
                    switch ($prev->type) {
                        case 2:
                            $prev->type = 4;
                            break;

                        case 1:
                        case 16:
                            // erlaubt 2(2) -> 2 * 2 | (2)(2) -> 2 * 2
                            $this->tokens[] = new Token(67, '*');
                            break;
                    }

                    break;
            }

            $this->tokens[] = $prev = new Token($type, $value);
        }
    }

    public function curr()
    {
        return current($this->tokens);
    }

    public function next()
    {
        return next($this->tokens);
    }

    public function prev()
    {
        return prev($this->tokens);
    }

    public function dump()
    {
        print_r($this->tokens);
    }

    public function peek()
    {
        $v = next($this->tokens);
        prev($this->tokens);

        return $v;
    }
}